// RedJey Graphics User Interface Library for J2ME Platform.
// Copyright (C) 2008 Yevgen Krapiva
//
// This library is free software; you can redistribute
// it and/or modify it under the terms of the
// GNU Lesser General Public License as published
// by the Free Software Foundation; either version 3.0
// of the License, or any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//

package com.incrediblesoft.redjey.font4me;

import javax.swing.*;
import javax.swing.event.*;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.event.*;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;

/**
 * @author Yevgen Krapiva<br>
 *         Homepage: http://code.google.com/p/redjey/<br>
 *         Feedback: ykrapiva@gmail.com<br>
 *         Copyright (C) 2008 Yevgen Krapiva<br>
 */


public class Font4ME extends JFrame implements ActionListener, ItemListener, ChangeListener, DocumentListener, MouseListener
{
    public static final String VER_STRING = "v1.0";
    public static final String SETTINGS_FILE_NAME = "font4me.cfg";
    //public static final String standartCharmap = " .,abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\"-+*/=!@#$%^&()[]{};:~'<>?";
    public static final String standartCharmap = " .,abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\"-+*/=!@$();:'?";
    public static final String defaultText = "I can see what you see not,\nVision milky then eyes rot,\nWhen you turn they will begun,\nWhispering their hidden song,\nThen you'll see what cannot be,\nShadows move where light should be,\nOut of darkness, out of mind,\nCast down into The Halls of the Blind.";

    String lastSaveDir = ".";
    String lastImportDir = ".";
    String lastCharmap = new String(standartCharmap);
    String lastText = new String(defaultText);

    JFileChooser import_fc = null;
    JFileChooser open_fc = null;
    JFileChooser save_fc = null;
    
    JButton importFontBtn = null;
    JButton colorBtn = null;
    JButton resetCharmapBtn = null;
    JButton saveFontBtn = null;
    JButton setTextBtn = null;
    JButton resetTextBtn = null;

    JLabel charSizeLabel = null;
    JSpinner charSizeSpin = null;
    JSpinner charXSpin = null;
    JSpinner fontSizeSpin;

    FontPreviewCanvas canvas = null;
    JColorChooser fcc = null, bcc = null;
    JFrame colorFrame = null;

    JCheckBox transparentColorBox = null;
    JCheckBox caseSensitiveBox = null;

    JComboBox canvasWidthBox = null;
    JComboBox canvasHeightBox = null;
    JComboBox fontsBox, stylesBox;

    JTextField charmapField = null;

    JMenu fileMenu;
    JMenuItem newMenuItem, openMenuItem, saveMenuItem, saveasMenuItem, quitMenuItem;

    String fontChoice;
    int styleChoice = 0;
    int sizeChoice = 12;

    //boolean antialised = false;
    //boolean transparentBackground = true;
    Color foregroundColor = new Color(0, 0, 0, 255);
    Color backgroundColor = null;
    char adjustedChar;

    String[] canvasWidthPatterns = new String[] { "128", "176", "240" };
    String[] canvasHeightPatterns = new String[] { "128", "160", "208", "320"};

    boolean doNotUpdateCharmapField;
    
    public static void main(String[] args)
    {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
    }

    /**
     * Contstructor
     * @param title widow title
     */
    public Font4ME(String title)
    {
        super(title);

        addWindowListener(
                new WindowAdapter()
                {
                    public void windowClosing(WindowEvent e)
                    {
                        saveSettings();
                        dispose();
                        System.exit(0);
                    }
                }
        );

        loadSettings();
    }

    /**
     * Creates UI
     */
    private static void createAndShowGUI()
    {
        //Use the Java look and feel.
        try
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            //UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
        } catch (Exception e) { }

        //Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(true);

        //Instantiate the controlling class.
        Font4ME frame = new Font4ME("RedJey Font4ME. Custom font creation tool "+VER_STRING);

        frame.createComponents();

        //Display the window.
        frame.pack();
        frame.setLocationRelativeTo(null); //center it
        frame.setVisible(true);

        frame.update();
    }

    /**
     * Creates inner components of this Frame
     */
    public void createComponents()
    {
        JPanel contentPane = (JPanel) getContentPane();

        JPanel topPanel = new JPanel();
        BoxLayout boxLayout = new BoxLayout(topPanel, BoxLayout.Y_AXIS);
        topPanel.setLayout(boxLayout);

        JPanel fontSettingsPanel = new JPanel();

        saveFontBtn = new JButton("Save font");
        saveFontBtn.addActionListener(this);
        importFontBtn = new JButton("Import font");
        importFontBtn.addActionListener(this);


        GraphicsEnvironment gEnv = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String[] fontNames = gEnv.getAvailableFontFamilyNames();
        fontsBox = new JComboBox(fontNames);      
        fontsBox.setMaximumRowCount(5);
        fontsBox.addItemListener(this);
        fontChoice = (String) fontsBox.getSelectedItem();

        String[] styleNames = {"Plain", "Bold", "Italic", "Bold Italic"};
        stylesBox = new JComboBox(styleNames);
        stylesBox.addItemListener(this);

        fontSizeSpin = new JSpinner(new SpinnerNumberModel(12, 6, 48, 1));
        fontSizeSpin.addChangeListener(this);

        colorBtn = new JButton("Color");
        colorBtn.addActionListener(this);

        fontSettingsPanel.add(saveFontBtn);
        fontSettingsPanel.add(importFontBtn);
        fontSettingsPanel.add(fontsBox);
        fontSettingsPanel.add(new JLabel("Style:"));
        fontSettingsPanel.add(stylesBox);
        fontSettingsPanel.add(new JLabel("Size:"));
        fontSettingsPanel.add(fontSizeSpin);
        fontSettingsPanel.add(colorBtn);

        JPanel canvasSettingsPanel = new JPanel();

        canvasWidthBox = new JComboBox(canvasWidthPatterns);
        canvasHeightBox = new JComboBox(canvasHeightPatterns);
        canvasWidthBox.setEditable(true);
        canvasHeightBox.setEditable(true);
        canvasWidthBox.addActionListener(this);
        canvasHeightBox.addActionListener(this);
        setTextBtn = new JButton("Edit text");
        resetTextBtn = new JButton("Reset text");
        setTextBtn.addActionListener(this);
        resetTextBtn.addActionListener(this);

        canvasSettingsPanel.add(new JLabel("Canvas size:"));
        canvasSettingsPanel.add(canvasWidthBox);
        canvasSettingsPanel.add(new JLabel("x"));
        canvasSettingsPanel.add(canvasHeightBox);
        canvasSettingsPanel.add(setTextBtn);
        canvasSettingsPanel.add(resetTextBtn);


        topPanel.add(fontSettingsPanel);
        topPanel.add(new JSeparator());
        topPanel.add(canvasSettingsPanel);
        
        JPanel canvasPanel = new JPanel(new BorderLayout());

        canvas = new FontPreviewCanvas();
        canvas.addMouseListener(this);
        canvas.setToolTipText("Click on character to adjust its settings.");
       // canvas.setBorder(new LineBorder(Color.red));
        canvasPanel.add(canvas, BorderLayout.CENTER);

        JPanel tmpPanel = new JPanel();
        charXSpin = new JSpinner(new SpinnerNumberModel(0, 0, 9999, 1));
        //charXSpin.setPreferredSize(new Dimension(40, charXSpin.getPreferredSize().height));
        charXSpin.addChangeListener(this);
        charXSpin.setToolTipText("This spinner changes the start position of character in font image. \n\nIf you see that some character appearance includes illegal pixels, try to change this value.");
        charSizeLabel = new JLabel("Character ''");
        charSizeSpin = new JSpinner(new SpinnerNumberModel(0, 0, 127, 1));
       // charSizeSpin.setPreferredSize(new Dimension(40, charSizeSpin.getPreferredSize().height));
        charSizeSpin.addChangeListener(this);
        charSizeSpin.setToolTipText("This spinner changes the width of character in font image. \n\nIf you see that some character appearance includes illegal pixels, try to change this value.");

        tmpPanel.add(charSizeLabel);
        tmpPanel.add(new JLabel("X: "));
        tmpPanel.add(charXSpin);
        tmpPanel.add(new JLabel("W: "));
        tmpPanel.add(charSizeSpin);
        canvasPanel.add(tmpPanel, BorderLayout.PAGE_END);

        charmapField = new JTextField(lastCharmap, 40);
        charmapField.getDocument().addDocumentListener(this);

        caseSensitiveBox = new JCheckBox("Font is case sensitive");
        caseSensitiveBox.setSelected(true);
        caseSensitiveBox.addItemListener(this);
        caseSensitiveBox.setAlignmentX(Component.LEFT_ALIGNMENT);
        //caseSensitiveBox.setAlignmentX(Component.CENTER_ALIGNMENT);
        //caseSensitiveBox.setAlignmentX(Component.RIGHT_ALIGNMENT);

        resetCharmapBtn = new JButton("Reset");
        resetCharmapBtn.addActionListener(this);

        JPanel charmapPanel = new JPanel();
        charmapPanel.add(new JLabel("Character map"));
        charmapPanel.add(charmapField);
        charmapPanel.add(resetCharmapBtn);
        charmapPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
        
        JPanel bottomPanel = new JPanel();
        boxLayout = new BoxLayout(bottomPanel, BoxLayout.PAGE_AXIS);
        bottomPanel.setLayout(boxLayout);

        JSeparator separator = new JSeparator();
        separator.setAlignmentX(LEFT_ALIGNMENT);
        bottomPanel.add(separator);
        bottomPanel.add(caseSensitiveBox);
        bottomPanel.add(charmapPanel);
        bottomPanel.setAlignmentX(LEFT_ALIGNMENT);


        //JLabel label = new JLabel("Copyright © 2008 Yevgen Krapiva");
        //Font font = label.getFont();
        //font = font.deriveFont(Font.ITALIC, font.getSize() - 2);
        //label.setFont(font);
        //label.setAlignmentX(LEFT_ALIGNMENT);
        //bottomPanel.add(label);

        JEditorPane jep = new JEditorPane("text/html", "<center>Copyright © 2008 Yevgen Krapiva <br> Visit <a href=\"http://code.google.com/p/redjey/\">http://code.google.com/p/redjey/</a></center>");
        Font font = jep.getFont();
        font = font.deriveFont(Font.ITALIC, font.getSize() - 2);
        jep.setFont(font);
        jep.setAlignmentX(LEFT_ALIGNMENT);
        jep.setEditable(false);
        jep.setOpaque(false);
        jep.addHyperlinkListener(new HyperlinkListener() {
		public void hyperlinkUpdate(HyperlinkEvent hle) {
			if (HyperlinkEvent.EventType.ACTIVATED.equals(hle.getEventType())) {
                try
                {
                    Desktop.getDesktop().browse(hle.getURL().toURI());
                }
                catch (Exception e)
                {
                }
            }
		}
	});
        bottomPanel.add(jep);
        //bottomPanel.add(new JLabel("<html><a href=\"http://code.google.com/p/redjey/\">http://code.google.com/p/redjey/</a></html>"));
        //bottomPanel.setAlignmentX(Component.LEFT_ALIGNMENT);

        contentPane.add(topPanel, BorderLayout.PAGE_START);
        contentPane.add(canvasPanel, BorderLayout.CENTER);
        contentPane.add(bottomPanel, BorderLayout.PAGE_END);

        fcc = new JColorChooser(foregroundColor);
        bcc = new JColorChooser();
        fcc.getSelectionModel().addChangeListener(this);
        bcc.getSelectionModel().addChangeListener(this);

        JPanel dumpPanel = new JPanel(new BorderLayout());
        transparentColorBox = new JCheckBox("Transparent background", true);
        transparentColorBox.addItemListener(this);
        dumpPanel.add(transparentColorBox, BorderLayout.NORTH);
        dumpPanel.add(bcc, BorderLayout.CENTER);

        JTabbedPane tabbedPane= new JTabbedPane();
        tabbedPane.addTab("Foreground", null, fcc);
        tabbedPane.addTab("Background", null, dumpPanel);
        colorFrame = new JFrame("Pick a color");
        colorFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        colorFrame.getContentPane().add(tabbedPane);
        colorFrame.pack();

       /* fileMenu = new JMenu("File");
        newMenuItem = new JMenuItem("New");
        openMenuItem = new JMenuItem("Open");
        //saveMenuItem = new JMenuItem("Save");
        saveasMenuItem = new JMenuItem("Save as");
        quitMenuItem = new JMenuItem("Quit");
        newMenuItem.addActionListener(this);
        openMenuItem.addActionListener(this);
        //saveMenuItem.addActionListener(this);
        saveasMenuItem.addActionListener(this);
        quitMenuItem.addActionListener(this);
        fileMenu.add(newMenuItem);
        fileMenu.add(openMenuItem);
        //fileMenu.add(saveMenuItem);
        fileMenu.add(saveasMenuItem);
        fileMenu.add(quitMenuItem);

        JMenuBar menuBar = new JMenuBar();
        menuBar.add(fileMenu);
        setJMenuBar(menuBar);*/

        canvasWidthBox.setSelectedItem("240");
        canvasHeightBox.setSelectedItem("320");
    }

    /**
     * Applies all changes to font settings, color etc.
     */
    public void update()
    {
        int newCanvasWidth = 0;
        int newCanvasHeight = 0;

        try
        {
            newCanvasWidth = Integer.parseInt(canvasWidthBox.getSelectedItem().toString());
        }
        catch (NumberFormatException e)
        {
            e.printStackTrace();
        }
        try
        {
            newCanvasHeight = Integer.parseInt(canvasHeightBox.getSelectedItem().toString());
        }
        catch (NumberFormatException e)
        {
            e.printStackTrace();
        }

        newCanvasWidth = Math.max(32, newCanvasWidth);
        newCanvasHeight = Math.max(32, newCanvasHeight);

        boolean contains = false;
        String s = ""+newCanvasWidth;

        for (int i=0; i<canvasWidthBox.getItemCount(); i++)
        {
            if (canvasWidthBox.getItemAt(i).toString().equals(s))
            {
                contains = true;
                break;
            }
        }

        if (!contains)
        {
            canvasWidthBox.addItem(s);
            canvasWidthBox.setSelectedItem(s);
        }

        contains = false;
        s = ""+newCanvasHeight;

        for (int i=0; i<canvasHeightBox.getItemCount(); i++)
        {
            if (canvasHeightBox.getItemAt(i).toString().equals(s))
            {
                contains = true;
                break;
            }
        }

        if (!contains)
        {
            canvasHeightBox.addItem(s);
            canvasHeightBox.setSelectedItem(s);
        }

        canvas.setCanvasWidth(newCanvasWidth);
        canvas.setCanvasHeight(newCanvasHeight);

        if (fontChoice != null)
        {
            Font font = new Font(fontChoice,styleChoice,sizeChoice);
            if (font != null)
            {
                canvas.setFont(font);
            }
        }

        if (lastText == null) lastText = new String(defaultText);

        canvas.setCharmap(charmapField.getText());
        canvas.setForegroundColor(foregroundColor);
        canvas.setBackgroundColor(backgroundColor);
        canvas.setPreferredSize(new Dimension(canvas.getPreferredSize().width, newCanvasHeight));
        canvas.setText(lastText);
        canvas.setFontCaseSensitive(caseSensitiveBox.isSelected());

        canvas.revalidate();
        canvas.repaint();
    }

    /**
     * Saves font
     */
    public void save(String absolutePath, String format)
    {
        String text = canvas.getCharmap();
        if (text == null || text.length() == 0) return;

        File f = null;
        FileOutputStream fos = null;

        try
        {
            String ext = getExtension(new File(absolutePath));

            if (format.equals("png"))
            {
                if (ext == null || !ext.equals("png"))
                    f = new File(absolutePath+".png");
                else f = new File(absolutePath);
            }
            else if (format.equals("fnt"))
            {
                if (ext == null || !ext.equals("fnt"))
                    f = new File(absolutePath+".fnt");
                else f = new File(absolutePath);
            }
            if (f != null)
            {
                if (format.equals("fnt"))
                {
                    byte[] fontData = canvas.getCustomFont().toByteArray();
                    if (fontData != null)
                    {
                        fos = new FileOutputStream(f);
                        fos.write(fontData);
                    }
                }

                if (format.equals("png"))
                {
                    BufferedImage image = canvas.getCustomFont().getImage();
                    ImageIO.write(image, "png", f);
                }
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            if (fos != null)
                try { fos.close(); } catch (IOException ex) { /* ignored */ };
        }
    }

    /*
       * Get the extension of a file.
       */
    public static String getExtension(File f)
    {
        String ext = null;
        String s = f.getName();
        int i = s.lastIndexOf('.');

        if (i > 0 &&  i < s.length() - 1)
        {
            ext = s.substring(i+1).toLowerCase();
        }

        return ext;
    }


    private void loadSettings()
    {
        DataInputStream dis = null;
        File file;

        file = new File(SETTINGS_FILE_NAME);
        if (!file.exists())  return;

        System.out.println("Loading settings from "+file.getAbsolutePath());

        try
        {
            dis = new DataInputStream(new FileInputStream(file));

            String key, value;

            while ((key = dis.readUTF()) != null && (value = dis.readUTF()) != null)
            {
                if (key.equals("LAST_SAVE_DIR"))
                    lastSaveDir = value;
                else if (key.equals("LAST_IMPORT_DIR"))
                    lastImportDir = value;
                else if (key.equals("LAST_CHARMAP"))
                    lastCharmap = value;
                else if (key.equals("LAST_TEXT"))
                    lastText = value;
            }
        }
        catch (Exception ex)
        {

        }
        finally
        {
            if (dis != null)
                try
                {
                    dis.close();
                }
                catch (IOException ex)
                {

                }
        }
    }

    /**
     * Saves settings
     */
    private void saveSettings()
    {
        DataOutputStream dos = null;
        File file;

        try
        {
            file = new File(SETTINGS_FILE_NAME);

            if (file.exists())
            {
                file.delete();
            }

            if (!file.createNewFile())
                return;

            dos = new DataOutputStream(new FileOutputStream(file));

            dos.writeUTF("LAST_SAVE_DIR");
            dos.writeUTF(lastSaveDir);
            dos.writeUTF("LAST_IMPORT_DIR");
            dos.writeUTF(lastImportDir);
            dos.writeUTF("LAST_CHARMAP");
            dos.writeUTF(charmapField.getText());
            dos.writeUTF("LAST_TEXT");
            dos.writeUTF(canvas.getText());

            System.out.println("Saved settings to "+file.getAbsolutePath());
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            if (dos != null)
                try
                {
                    dos.close();
                }
                catch (IOException ex)
                {

                }
        }

    }
    

    public void actionPerformed(ActionEvent e)
    {
        Object source = e.getSource();
        //System.out.println("Action perfomed on: "+source);
        
        if (source == importFontBtn)
        {
            //Set up the file chooser.
            if (import_fc == null)
            {
                import_fc = new JFileChooser();
                import_fc.setCurrentDirectory(new File(lastImportDir));
                //Add a custom file filter and disable the default
                //(Accept All) file filter.
                import_fc.addChoosableFileFilter(new FontFilter(FontFilter.TTF_FILTER));
                import_fc.setAcceptAllFileFilterUsed(false);
            }

            //Show it.
            int returnVal = import_fc.showDialog(this, "Open");

            //Process the results.
            if (returnVal == JFileChooser.APPROVE_OPTION)
            {
                File file = import_fc.getSelectedFile();
                try
                {
                    lastImportDir = file.getParentFile().getCanonicalPath();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }

                try
                {
                    Font font = Font.createFont(Font.TRUETYPE_FONT, file);
                    fontsBox.addItem(font.getName());
                    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
                    ge.registerFont(font);
                    fontsBox.setSelectedItem(font.getName());
                    update();
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
            }


            //Reset the file chooser for the next time it's shown.
            import_fc.setSelectedFile(null);
        }
        else if (source == saveFontBtn)
        {
            //Set up the file chooser.
            if (save_fc == null)
            {
                save_fc = new JFileChooser();
                save_fc.setCurrentDirectory(new File(lastSaveDir));
                //Add a custom file filter and disable the default
                //(Accept All) file filter.
                save_fc.addChoosableFileFilter(new FontFilter(FontFilter.PNG_FILTER));
                save_fc.addChoosableFileFilter(new FontFilter(FontFilter.FNT_FILTER));
                save_fc.setAcceptAllFileFilterUsed(false);
            }

            //Show it.
            int returnVal = save_fc.showDialog(this, "Save");

            //Process the results.
            if (returnVal == JFileChooser.APPROVE_OPTION)
            {
                File file = save_fc.getSelectedFile();
                FontFilter selectedFilter = (FontFilter) save_fc.getFileFilter();
                String format = selectedFilter.getType() == FontFilter.FNT_FILTER ? "fnt" : "png";

                try
                {
                    lastSaveDir = file.getParentFile().getCanonicalPath();
                }
                catch (IOException e1)
                {
                    e1.printStackTrace();
                }

                save(file.getAbsolutePath(), format);
            }
        }
        else if (source == colorBtn)
        {
            if (!colorFrame.isVisible())
                colorFrame.setVisible(true);
        }
        else if (source == resetCharmapBtn)
        {
            charmapField.setText(standartCharmap);
        }
        else if (source == setTextBtn)
        {
            JFrame frame=new JFrame("Edit text");
            final JTextArea textArea=new JTextArea();
            JScrollPane scrollPane = new JScrollPane(textArea);
            Container contentPane = frame.getContentPane();
            contentPane.add(scrollPane);
            scrollPane.setPreferredSize(new Dimension(320, 200));
            frame.addWindowListener(new WindowAdapter(){
                public void windowClosing(WindowEvent e)
                {
                    lastText = (textArea.getText());
                    ((JFrame) e.getSource()).dispose();
                    update();
                }
            });
            frame.pack();
            frame.setLocationRelativeTo(this); //center it
            frame.setVisible(true);

            textArea.setText(canvas.getText());
        }
        else if (source == resetTextBtn)
        {
            lastText = new String(defaultText);
            update();
        }

        //System.out.println("actionPerfomed -> "+source);
        update();
    }

    public void itemStateChanged(ItemEvent e)
    {
        if (e.getSource() == fontsBox)
        {
            fontChoice = (String) fontsBox.getSelectedItem();
            update();
        }
        else if (e.getSource() == stylesBox)
        {
            styleChoice = stylesBox.getSelectedIndex();
            update();
        }
        else if (e.getSource() == transparentColorBox)
        {
            //transparentBackground = transparentColorBox.isSelected();
            backgroundColor = transparentColorBox.isSelected() ? null : bcc.getColor();
            update();
        }
        else if (e.getSource() == caseSensitiveBox && !caseSensitiveBox.isSelected())
        {
            StringBuffer newText = new StringBuffer();
            String text = charmapField.getText();

            for (int i=0; i<text.length(); i++)
                if (!Character.isLetter(text.charAt(i)) || Character.isLowerCase(text.charAt(i)))
                    newText.append(text.charAt(i));

            charmapField.setText(newText.toString());
            update();
        }
    }

    public void stateChanged(ChangeEvent e)
    {
        Object source = e.getSource();

        if (source == fontSizeSpin)
        {
            try
            {
                String size = fontSizeSpin.getModel().getValue().toString();
                sizeChoice = Integer.parseInt(size);
                update();
            }
            catch (NumberFormatException nfe)
            {
                // ыыы
            }
        }
        else if (source == charXSpin)
        {
            try
            {
                short value = Short.parseShort(charXSpin.getModel().getValue().toString());
                value = (short) Math.max(value, 0);
                CustomFont customFont = canvas.getCustomFont();
                int index = customFont.getCharmap().indexOf(adjustedChar);
                if (index != -1)
                {
                    customFont.xPositions[index] = value;
                    canvas.repaint();
                }
            }
            catch (NumberFormatException ex)
            {

            }
        }
        else if (source == charSizeSpin)
        {
            try
            {
                int value = Integer.parseInt(charSizeSpin.getModel().getValue().toString());
                value = Math.min(value, Byte.MAX_VALUE);
                value = Math.max(0, value);
                CustomFont customFont = canvas.getCustomFont();
                int index = customFont.getCharmap().indexOf(adjustedChar);
                if (index != -1)
                {
                    customFont.characterWidths[index] = (byte) value;
                    canvas.repaint();
                }
            }
            catch (NumberFormatException ex)
            {

            }
        }
        else //if (source == fcc || source == bcc)
        {
            foregroundColor = fcc.getColor();
            backgroundColor = transparentColorBox.isSelected() ? null : bcc.getColor();
            update();
        }
    }

    public void insertUpdate(DocumentEvent e)
    {
        if (!doNotUpdateCharmapField)
        javax.swing.SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                int caretPos = charmapField.getCaretPosition();
                setCharmapText(charmapField.getText());
                update();
                charmapField.setCaretPosition(caretPos);
            }
        });
    }

    public void removeUpdate(DocumentEvent e)
    {
        if (!doNotUpdateCharmapField)
        javax.swing.SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                int caretPos = charmapField.getCaretPosition();
                setCharmapText(charmapField.getText());
                update();
                charmapField.setCaretPosition(caretPos);
            }
        });
    }

    public void changedUpdate(DocumentEvent e)
    {
        if (!doNotUpdateCharmapField)
        javax.swing.SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                int caretPos = charmapField.getCaretPosition();
                setCharmapText(charmapField.getText());
                update();
                charmapField.setCaretPosition(caretPos);
            }
        });
    }

    public void mouseClicked(MouseEvent e)
    {

    }

    public void mousePressed(MouseEvent e)
    {
        char ch= canvas.getCharAt(e.getX(), e.getY());
        if (ch == 0) return;

        adjustedChar = ch;
        charSizeLabel.setText("Character '"+adjustedChar+"'");

        CustomFont customFont = canvas.getCustomFont();
        int index = customFont.getCharmap().indexOf(ch);
        int charWidth = customFont.characterWidths[index];
        int charX = customFont.xPositions[index];
        charSizeSpin.getModel().setValue(charWidth);
        charXSpin.getModel().setValue(charX);
       
    }

    public void mouseReleased(MouseEvent e)
    {

    }

    public void mouseEntered(MouseEvent e)
    {

    }

    public void mouseExited(MouseEvent e)
    {

    }

    private void addCharToCharmap(char ch)
    {
        char newCh = caseSensitiveBox.isSelected() ? ch : Character.toLowerCase(ch);
        if (charmapField.getText().indexOf(newCh) == -1)
            charmapField.setText(charmapField.getText() + newCh);
    }

    private void setCharmapText(String text)
    {
        doNotUpdateCharmapField = true;

        charmapField.setText(null);
        for (int i=0; i<text.length(); i++)
            addCharToCharmap(text.charAt(i));

        doNotUpdateCharmapField = false;
    }
}
